1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.truth.Truth.assertThat;
20  
21  import com.google.common.collect.testing.features.CollectionFeature;
22  import com.google.common.collect.testing.features.CollectionSize;
23  import com.google.common.collect.testing.features.MapFeature;
24  import com.google.common.collect.testing.google.SetMultimapTestSuiteBuilder;
25  import com.google.common.collect.testing.google.TestStringSetMultimapGenerator;
26  
27  import junit.framework.Test;
28  import junit.framework.TestCase;
29  import junit.framework.TestSuite;
30  
31  import java.io.Serializable;
32  import java.util.Arrays;
33  import java.util.Collection;
34  import java.util.Map;
35  import java.util.Map.Entry;
36  import java.util.RandomAccess;
37  import java.util.Set;
38  
39  import javax.annotation.Nullable;
40  
41  /**
42   * Tests for {@code Synchronized#multimap}.
43   *
44   * @author Mike Bostock
45   */
46  public class SynchronizedMultimapTest extends TestCase {
47    
48    public static Test suite() {
49      TestSuite suite = new TestSuite();
50      suite.addTestSuite(SynchronizedMultimapTest.class);
51      suite.addTest(SetMultimapTestSuiteBuilder.using(new TestStringSetMultimapGenerator() {
52          @Override
53          protected SetMultimap<String, String> create(Entry<String, String>[] entries) {
54            TestMultimap<String, String> inner = new TestMultimap<String, String>();
55            SetMultimap<String, String> outer = Synchronized.setMultimap(inner, inner.mutex);
56            for (Entry<String, String> entry : entries) {
57              outer.put(entry.getKey(), entry.getValue());
58            }
59            return outer;
60          }
61        })
62        .named("Synchronized.setMultimap")
63        .withFeatures(MapFeature.GENERAL_PURPOSE,
64            CollectionSize.ANY,
65            CollectionFeature.SERIALIZABLE,
66            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
67            MapFeature.ALLOWS_NULL_KEYS,
68            MapFeature.ALLOWS_NULL_VALUES,
69            MapFeature.ALLOWS_ANY_NULL_QUERIES)
70        .createTestSuite());
71      return suite;
72    }
73  
74    private static final class TestMultimap<K, V> extends ForwardingSetMultimap<K, V>
75        implements Serializable {
76      final SetMultimap<K, V> delegate = HashMultimap.create();
77      public final Object mutex = new Integer(1); // something Serializable
78  
79      @Override protected SetMultimap<K, V> delegate() {
80        return delegate;
81      }
82  
83      @Override public String toString() {
84        assertTrue(Thread.holdsLock(mutex));
85        return super.toString();
86      }
87  
88      @Override public boolean equals(@Nullable Object o) {
89        assertTrue(Thread.holdsLock(mutex));
90        return super.equals(o);
91      }
92  
93      @Override public int hashCode() {
94        assertTrue(Thread.holdsLock(mutex));
95        return super.hashCode();
96      }
97  
98      @Override public int size() {
99        assertTrue(Thread.holdsLock(mutex));
100       return super.size();
101     }
102 
103     @Override public boolean isEmpty() {
104       assertTrue(Thread.holdsLock(mutex));
105       return super.isEmpty();
106     }
107 
108     @Override public boolean containsKey(@Nullable Object key) {
109       assertTrue(Thread.holdsLock(mutex));
110       return super.containsKey(key);
111     }
112 
113     @Override public boolean containsValue(@Nullable Object value) {
114       assertTrue(Thread.holdsLock(mutex));
115       return super.containsValue(value);
116     }
117 
118     @Override public boolean containsEntry(@Nullable Object key,
119         @Nullable Object value) {
120       assertTrue(Thread.holdsLock(mutex));
121       return super.containsEntry(key, value);
122     }
123 
124     @Override public Set<V> get(@Nullable K key) {
125       assertTrue(Thread.holdsLock(mutex));
126       /* TODO: verify that the Collection is also synchronized? */
127       return super.get(key);
128     }
129 
130     @Override public boolean put(K key, V value) {
131       assertTrue(Thread.holdsLock(mutex));
132       return super.put(key, value);
133     }
134 
135     @Override public boolean putAll(@Nullable K key,
136         Iterable<? extends V> values) {
137       assertTrue(Thread.holdsLock(mutex));
138       return super.putAll(key, values);
139     }
140 
141     @Override public boolean putAll(Multimap<? extends K, ? extends V> map) {
142       assertTrue(Thread.holdsLock(mutex));
143       return super.putAll(map);
144     }
145 
146     @Override public Set<V> replaceValues(@Nullable K key,
147         Iterable<? extends V> values) {
148       assertTrue(Thread.holdsLock(mutex));
149       return super.replaceValues(key, values);
150     }
151 
152     @Override public boolean remove(@Nullable Object key,
153         @Nullable Object value) {
154       assertTrue(Thread.holdsLock(mutex));
155       return super.remove(key, value);
156     }
157 
158     @Override public Set<V> removeAll(@Nullable Object key) {
159       assertTrue(Thread.holdsLock(mutex));
160       return super.removeAll(key);
161     }
162 
163     @Override public void clear() {
164       assertTrue(Thread.holdsLock(mutex));
165       super.clear();
166     }
167 
168     @Override public Set<K> keySet() {
169       assertTrue(Thread.holdsLock(mutex));
170       /* TODO: verify that the Set is also synchronized? */
171       return super.keySet();
172     }
173 
174     @Override public Multiset<K> keys() {
175       assertTrue(Thread.holdsLock(mutex));
176       /* TODO: verify that the Set is also synchronized? */
177       return super.keys();
178     }
179 
180     @Override public Collection<V> values() {
181       assertTrue(Thread.holdsLock(mutex));
182       /* TODO: verify that the Collection is also synchronized? */
183       return super.values();
184     }
185 
186     @Override public Set<Map.Entry<K, V>> entries() {
187       assertTrue(Thread.holdsLock(mutex));
188       /* TODO: verify that the Collection is also synchronized? */
189       return super.entries();
190     }
191 
192     @Override public Map<K, Collection<V>> asMap() {
193       assertTrue(Thread.holdsLock(mutex));
194       /* TODO: verify that the Map is also synchronized? */
195       return super.asMap();
196     }
197 
198     private static final long serialVersionUID = 0;
199   }
200 
201   public void testSynchronizedListMultimap() {
202     ListMultimap<String, Integer> multimap
203         = Multimaps.synchronizedListMultimap(
204             ArrayListMultimap.<String, Integer>create());
205     multimap.putAll("foo", Arrays.asList(3, -1, 2, 4, 1));
206     multimap.putAll("bar", Arrays.asList(1, 2, 3, 1));
207     assertThat(multimap.removeAll("foo")).has().exactly(3, -1, 2, 4, 1).inOrder();
208     assertFalse(multimap.containsKey("foo"));
209     assertThat(multimap.replaceValues("bar", Arrays.asList(6, 5)))
210         .has().exactly(1, 2, 3, 1).inOrder();
211     assertThat(multimap.get("bar")).has().exactly(6, 5).inOrder();
212   }
213 
214   public void testSynchronizedSortedSetMultimap() {
215     SortedSetMultimap<String, Integer> multimap
216         = Multimaps.synchronizedSortedSetMultimap(
217             TreeMultimap.<String, Integer>create());
218     multimap.putAll("foo", Arrays.asList(3, -1, 2, 4, 1));
219     multimap.putAll("bar", Arrays.asList(1, 2, 3, 1));
220     assertThat(multimap.removeAll("foo")).has().exactly(-1, 1, 2, 3, 4).inOrder();
221     assertFalse(multimap.containsKey("foo"));
222     assertThat(multimap.replaceValues("bar", Arrays.asList(6, 5)))
223         .has().exactly(1, 2, 3).inOrder();
224     assertThat(multimap.get("bar")).has().exactly(5, 6).inOrder();
225   }
226 
227   public void testSynchronizedArrayListMultimapRandomAccess() {
228     ListMultimap<String, Integer> delegate = ArrayListMultimap.create();
229     delegate.put("foo", 1);
230     delegate.put("foo", 3);
231     ListMultimap<String, Integer> multimap
232         = Multimaps.synchronizedListMultimap(delegate);
233     assertTrue(multimap.get("foo") instanceof RandomAccess);
234     assertTrue(multimap.get("bar") instanceof RandomAccess);
235   }
236 
237   public void testSynchronizedLinkedListMultimapRandomAccess() {
238     ListMultimap<String, Integer> delegate = LinkedListMultimap.create();
239     delegate.put("foo", 1);
240     delegate.put("foo", 3);
241     ListMultimap<String, Integer> multimap
242         = Multimaps.synchronizedListMultimap(delegate);
243     assertFalse(multimap.get("foo") instanceof RandomAccess);
244     assertFalse(multimap.get("bar") instanceof RandomAccess);
245   }
246 }